#!/bin/sh

# This script is indended to provide GNU autoconf compatible behavior.
# Do not rely too much on it. If unsure, go edit Makefile and/or config.h

# Unlike actualy autoconf scripts, this script does not test anything.
# Testing makes little sense for a small, very linux-specific tool
# like sninit which has no way of handling unusual evironments.

unset prefix sbindir mandir man5dir man8dir sysconfdir

# configure uses lower-case variables to store "kinda-default" values
# while resp. upper-case variables are used for unconditional overrides
unset cc ar as strip cflags asflags ldflags
unset hostcc hostcflags
unset target host diet
unset usecc libc libs null

optimize=auto
unset nommu lto debug depend builtin quiet devmode
unset initctl inittab run s cgbase logdir blocks run extra
syslog=/dev/log

warn() { echo "$@" >&2; }
die() { echo "$@" >&2; exit 1; }
dieifnot() { test -n "$1" && return; shift; die "$@"; }
argreq() { dieifnot "$val" "$arg: value must be supplied"; }

usage() {
cat <<END
Usage: ./configure [options]

Options take either "key" or "key=val" forms.
For compatibility, --key=val and --with-key=val are accepted in most cases.

Installation directories:
	prefix=		overall prefix []
	sbindir=	system binaries directory [\$prefix/sbin]
	sysconfdir=	system configuration directory [\$prefix/etc]
	mandir=		man pages directory [\$prefix/share/man]

Runtime paths configuration:
	initctl=	init control socket [@initctl]
	inittab=	inittab [\$sysconfdir/inittab]
	initdir=	initdir [\$sysconfdir/initdir]

	disable-initdir	build init without initdir support
	disable-inittab	build init without any kind of runtime configuration

Target configuration:
	target=A	use \$A toolchain; in particular, use \$A-gcc
	build=B		use \$B toolchain for build-time executable
			(build= is only useful with builtin=)
	CC=		C compiler [gcc]
	AS=		assembler [gcc -c]
	AR=		archiver [ar]
			(as and ar are only used for bundled libc)

	nommu		enable tweaks for non-MMU targets (uClinux)

	lto		use link-time optimization (GCC only)
	debug		generate debug symbols
	depend		generate dependencies using \$CC -MD

	devel		disable optimization, generate debug symbols and dependencies,
			set inittab=etc/inittab and initdir=etc/initdir
			for testing and/or development

	qemu=A		use qemu-A to run tests

	quiet		do not print config summary to stdout

Libc selection (see INSTALL for explaination):
	arch=L		use bundled libc for target arch \$L
			possible L values: arm arm64 mips x86 x86_64 x86_32
	dietlibc	build with diet gcc
	musl-gcc	build with musl-gcc
	musl		use system libc, assume it to be musl
	glibc		use system libc, assume it to be GNU libc
	uclibc		use system libc, assume it to be uClibc

Init configuration:
	builtin=F	parse and compile \$F as built-in inittab
			(see doc/builtin.txt)

If not sure about configure usage, check comments in config.mk and config.h,
then edit those files directly.
END
exit 0
}

while [ $# -gt 0 ]; do
	arg="$1"; shift;
	# Separate LHS and RHS in --option=value or VAR=val cases
	case "$arg" in
		*=*)
			val=`echo "$arg" | sed -e 's/^[^=]*=//'`
			arg=`echo "$arg" | sed -e 's/=.*//'`
			;;
		*)
			val="" ;;
	esac

	# Now the arguments. We're aiming at being moderately autoconf-compatible,
	# so "--with-dietlibc" is allowed along with much more logical "dietlibc"
	# There will be some "extra" options like "with-dietlibc" but that's ok.
	#
	# Dedicated variable is used to produce correct error messages,
	# i.e. "--foo: argument needed" not "foo: argument needed".

	arq=`echo "$arg" | sed -e 's/^--//' | sed -e 's/^with-//'`

	case "$arq" in
		help) usage ;;

		sysconfdir|configdir) sysconfdir=$val ;;
		sbindir) sbindir=$val ;;
		mandir) mandir=$val ;;
		man5dir) mandir=$val ;;
		man8dir) mandir=$val ;;
		prefix) prefix=$val ;;
		logdir) logdir=$val ;;
		cgbase) cgbase=$val ;;
		exec-prefix) ;;

		s) s=s ;;
		sn|sn) s=sn ;;

		initctl) argreq; initctl=$val ;;
		inittab) argreq; inittab=$val ;;
		initdir) argreq; initdir=$val ;;
		builtin) argreq; builtin=$val ;;

		target)     argreq; target=$val; cross=$target- ;;
		build|host) argreq; hostcc=$val-gcc ;;
		hostcc)     argreq; hostcc=$val ;;

		cc) argreq; cc=${val:-cc} ;;
		as) argreq; as=$val ;;
		ar) argreq; ar=$val ;;
		strip) argreq; strip=$val ;;

		gcc)   usecc=gcc ;;
		clang) usecc=clang ;;

		cflags) cflags=$val ;;
		asflags) asflags=$val ;;
		ldflags) ldflags=$val ;;
		hostcflags) hostcflags=$val ;;

		musl-gcc) cc=musl-gcc; libc=musl ;;
		diet) diet=${val:-diet}; libc=dietlibc ;;

		bundled) libc=$arg ;; # arch will be decided later
		arch) argreq; libc=bundled; arch="$val" ;;

		glibc|uclibc|musl|dietlibc) libc=$arg ;;
		dietlibc-fixed|fixed) libc=diet-fixed ;;

		disable-inittab|no-inittab) inittab=- ;;
		disable-initdir|no-initdir) initdir=- ;;

		enable-debug|debug) debug=${val:-"-Wall -g"} ;;
		enable-lto|lto) lto=${val:-"-flto"} ;;
		optimization|O|optimize) optimize=${val:-auto} ;;
		disable-optimization|no-O) optimize= ; lto= ;;
		mmu) nommu= ;;
		without-mmu|nommu) nommu=1 ;;
		depend) depend=${val:--MD} ;;
		quiet) quiet=1 ;;
		run) argreq; run="$val" ;;
		qemu) test -n "$val" && run="qemu-$val" || run="auto" ;;

		devel)
			s=
			inittab=etc/inittab
			initdir=etc/initdir
			initctl=@initest
			syslog=sbin/devlog
			logdir=sbin/log
			optimize=
			debug="-Wall -g"
			depend="-MD"
			devmode=1
			extra=sbin
			;;

		with-*|without-*|enable-*|disable-*)
			# autoconf compatibility; buildroot supplies a lot of generic options
			# to configure, expecting unsupported ones to be ignored
			warn "Warning: unused option $arg"
			;;
		CC|AR|AS|HOSTCC|RUN) argreq
			eval "$arq='$val'"
			;;
		CFLAGS|ASFLAGS|LDFLAGS|LIBS)
			eval "$arq='$val'"
			;;
		*)
			die "Unknown option $arg"
			;;
	esac
done

# The following could be done in the loop above, if not for option order issues.
# i.e. --with-clang --with-dietlibc vs --with-dietlibc --with-clang

# Decide which compiler to use if we were left with the choice
case "$cc:${usecc:-gcc}" in
	:cc)	cc=${cross}cc ;;
	:gcc)	cc=${cross}gcc ;;
	:clang)	cc=clang${target:+ -arch $target} ;;
esac

test -n "$diet" && cc="$diet $cc"

# Decide which binutils to use
ar=${ar:-${cross}ar}
as=${as:-\$(CC) -c}	# this will go straight into Makefile
strip=${strip:-${cross}strip}

# Try to guess target architecture
# This should be done *after* deciding $CC of course
ccarch=`${CC:-$cc} -dumpmachine 2>/dev/null`
test -z "$ccarch" -a -n "$target" && ccarch="$target"

# Check if we're trying to build x86 on an x86_64 host
case "$arch:$ccarch" in
	x86:x86_64*)
		cc="$cc -m32 -march=i686"
		;;
	x86_32:x86_64*)
		cc="$cc -mx32"
		;;
esac

# See if we'd like to use bundled libc by default,
# and try to guess arch if bundled was specified without an arg
if [ -z "$libc" -o "$libc" = 'bundled' ]; then
	case "$arch:$ccarch" in
		:x86_64*)  arch=x86_64 ;;
		# x86_32 is *not* reported as such and must be specified explicitly!
		:x86*)     arch=x86 ;;
		:i?86*)    arch=x86 ;;
		:arm*)     arch=arm ;;
		:aarch64*) arch=arm64 ;;
		:mips64*) ;; # not supported atm
		:mips*)    arch=mips ;;
	esac
fi; if [ "$libc" = 'bundled' -a -z "$arch" ]; then
	die "Can't guess target architecture, use arch= option"
elif [ -n "$arch" -a ! -d "libc/$arch" ]; then
	die "Target $arch is not supported by the bundled libc"
elif [ -z "$libc" -a -n "$arch" ]; then
	libc=bundled
elif [ -z "$libc" ]; then
	libc=glibc
fi

# GCC has a bad habit of skipping libgcc with -nostdlib,
# which must be accounted for.
cckind=`${CC:-$cc} --version 2>/dev/null | head -1`
case "$libc:$cckind" in
	bundled:*gcc*) libs="-lgcc" ;;
esac

# Handle optimization
if [ "$optimize" = 'auto' ]; then
	optcommon="-Os -fno-asynchronous-unwind-tables"
	case "$ccarch" in
		# stdarg + x86 + SSE results in xmm* register handling bloat
		# despite the fact those are never used in sninit
		x86*) optimize="$optcommon -mno-sse" ;;
		# without any better ideas, just optimize for size
		*) optimize="$optcommon" ;;
	esac
fi

# Static PIE viability depends on toolchain configuration.
# SSP, if enabled, needs some(?) libc support which the bundled
# libc doesn't provide at this moment.
if [ "$libc" = "bundled" ]; then
	cflags="${cflags:+$cflags }-fno-stack-protector"
	ldflags="-static -fno-pie${ldflags:+ $ldflags}"
fi

# Join parts of CFLAGS into a single variable
if [ -n "$optimize" ]; then
	cflags=${cflags:+$cflags }$optimize
fi
if [ -n "$lto" ]; then
	cflags=${cflags:+$cflags }$lto
	ldflags=${ldflags:+$ldflags }$lto
fi
if [ -n "$debug" ]; then
	cflags=${cflags:+$cflags }$debug
	asflags=${asflags:+$asflags }-g
fi
if [ -n "$depend" ]; then
	cflags=${cflags:+$cflags }$depend
fi

# Check which blocks init needs

case "$inittab" in
	-) blocks="null" ;;
	*) blocks="conf" ;;
esac
case "$libc" in
	bundled)      blocks="$blocks sys" ;;
	glibc|uclibc) blocks="$blocks dents" ;;
	diet-fixed)   blocks="$blocks sys" ;;
	dietlibc)     blocks="$blocks sys ppoll unshare"; libs="-lcompat" ;;
esac

# Try to guess which qemu to use if this was requested
if [ "$run" = 'auto' ]; then
	cclead=`echo "$ccarch" | sed -e 's/-.*//' -e 's/[^A-Za-z0-9_-]//'`
	if [ -z "$cclead" -a -n "$target" ]; then
		cclead=`echo "$target" | sed -e 's/-.*//'`
	fi
	qemu="qemu-$cclead"
	if "$qemu" -version >& /dev/null; then
		run="$qemu"
	elif [ -n "$cclead" ]; then
		die "Can't find qemu for $cclead"
	else
		die "Can't guess target architecture for qemu"
	fi
fi

# Allow complete overwriting for these variables
CC=${CC:-$cc}
AS=${AS:-$as}
AR=${AR:-$ar}
STRIP=${STRIP:-$strip}
CFLAGS=${CFLAGS:-$cflags}
ASFLAGS=${ASFLAGS:-$asflags}
LDFLAGS=${LDFLAGS:-$ldflags}
LIBS=${LIBS:-$libs}
HOSTCC=${HOSTCC:-$hostcc}
RUN=${RUN:-$run}

# Default values for directory options, defined late because of $prefix dependency
subprefix=${prefix:-/usr}
prefix=${prefix:-/}
test "$prefix" = '/' && prefix=''
mandir=${mandir:-$subprefix/share/man}
man5dir=${man5dir:-$mandir/man5}
man8dir=${man8dir:-$mandir/man8}
sbindir=${sbindir:-$prefix/sbin}
cgbase=${cgbase:-/sys/fs/cgroup}
logdir=${logdir:-/var/log}
sysconfdir=${sysconfdir:-$prefix/etc}
initctl=${initctl:-@initctl}
test "$inittab" != '-' && inittab=${inittab:-$sysconfdir/inittab} || unset inittab
test "$initdir" != '-' && initdir=${initdir:-$sysconfdir/initdir} || unset initdir

sed -i \
	-e "/^ARCH :=/s#:=.*#:= $arch#"\
	-e "/^CC :=/s#:=.*#:= $CC#"\
	-e "/^AS :=/s#:=.*#:= $AS#"\
	-e "/^AR :=/s#:=.*#:= $AR#"\
	-e "/^STRIP :=/s#:=.*#:= $STRIP#"\
	-e "/^CFLAGS :=/s#:=.*#:= $CFLAGS#"\
	-e "/^ASFLAGS :=/s#:=.*#:= $ASFLAGS#"\
	-e "/^LDFLAGS :=/s#:=.*#:= $LDFLAGS#"\
	-e "/^LIBS :=/s#:=.*#:= $LIBS#"\
	-e "/^builtin :=/s#:=.*#:= $builtin#"\
	-e "/^HOSTCC :=/s#:=.*#:= $HOSTCC#"\
	-e "/^RUN :=/s#:=.*#:= $RUN#"\
	-e "/^s :=/s#:=.*#:= $s#"\
	-e "/^extra :=/s#:=.*#:= $extra#"\
	-e "/^initblocks :=/s#:=.*#:= $blocks#"\
	config.mk

unset nt; test -z "$inittab" && nt="//"
unset nd; test -z "$initdir" && nd="//"
unset mm; test -z "$nommu" && mm="//"
unset dm; test -z "$devmode" && dm="//"
sed -i \
	-e "/^.*#define INITTAB/s@.*@$nt#define INITTAB \"$inittab\"@" \
	-e "/^.*#define INITDIR/s@.*@$nd#define INITDIR \"$initdir\"@" \
	-e "/#define INITCTL/s#\".*#\"$initctl\"#" \
	-e "/#define SYSLOG/s#\".*#\"$syslog\"#" \
	-e "/^.*#define NOMMU/s@.*#define@$mm#define@" \
	-e "/^.*#define DEVMODE/s@.*#define@$dm#define@" \
	config.h

sed -i \
	-e "/#define CGBASE/s#\".*#\"$cgbase\"#" \
	-e "/#define LOGDIR/s#\".*#\"$logdir\"#" \
	run.h

test -n "$quiet" && exit

echo "Configuration updated. Check the values below, then run make."
echo
test -n "$arch" && \
 echo "	ARCH=$arch"
echo "	CC=$CC"
test -n "$arch" -a "$AS" != "\$(CC) -c" && \
 echo "	AS=$AS"
echo "	CFLAGS=$CFLAGS"
test -n "$arch" -a "$AS" != "\$(CC) -c" && \
 echo "	ASFLAGS=$ASFLAGS"
echo "	LDFLAGS=$LDFLAGS"
echo "	LIBS=$LIBS"
test -n "$RUN" && \
 echo "	RUN=$RUN"
echo
echo "	initblocks: $blocks"
echo
echo "	inittab location: $inittab"
echo "	initdir location: $initdir"
echo "	control socket name: $initctl"
echo
echo "	cgroups path: $cgbase"
echo "	log path: $logdir"
echo
echo "	binaries path: $sbindir"
echo "	executable names: ${s}init ${s}telinit ${s}run"
echo "	man section 5 path: $man5dir"
echo "	man section 8 path: $man8dir"
echo
